import { assetManager, instantiate, isValid, Prefab, sp, Sprite, SpriteFrame, UIOpacity, UITransform } from 'cc';
import { EcsSystem, filter } from 'db://pkg/@gamex/cc-ecs';
import { NodeComponent } from '../component/NodeComponent';
import { RenderComponent, RenderType } from '../component/RenderComponent';

export class RenderSystem extends EcsSystem {
    private nodeFilter = filter.all(NodeComponent);
    private renderFilter = filter.all(RenderComponent);

    protected beforeUpdate() {
        this.query(this.nodeFilter).forEach(entity => {
            entity.getComponents(NodeComponent).forEach(nodeComp => {
                const node = entity.node;

                if (nodeComp.active !== node.active) {
                    nodeComp.active = node.active;
                }
                if (!nodeComp.active) {
                    NodeComponent.systemHandle(nodeComp, false);
                    return;
                }
                if (nodeComp.refreshPosition) {
                    node.setPosition(nodeComp.x, nodeComp.y, nodeComp.z);
                }
                if (nodeComp.refreshScale) {
                    node.setScale(nodeComp.scaleX, nodeComp.scaleY, nodeComp.scaleZ);
                }
                if (nodeComp.refreshAngle) {
                    node.angle = nodeComp.angle;
                }
                if (nodeComp.refreshOpacity) {
                    const uiOpacity = node.getComponent(UIOpacity) || node.addComponent(UIOpacity);
                    uiOpacity.opacity = nodeComp.opacity;
                }
                if (nodeComp.refreshAnchor) {
                    const uiTransform = node.getComponent(UITransform) || node.addComponent(UITransform);
                    uiTransform.setAnchorPoint(nodeComp.anchorX, nodeComp.anchorY);
                }
                if (nodeComp.refreshSize) {
                    const uiTransform = node.getComponent(UITransform) || node.addComponent(UITransform);
                    uiTransform.setContentSize(nodeComp.width, nodeComp.height);
                }

                NodeComponent.systemHandle(nodeComp);
            });
        });

        this.query(this.renderFilter).forEach(entity => {
            entity.getComponents(RenderComponent).forEach(renderComp => {
                if (renderComp.refreshAttr) {
                    renderComp.renderNode.setPosition(renderComp.offset);
                    renderComp.renderNode.setScale(renderComp.scale);
                }
                if (renderComp.renderType === RenderType.Texture) {
                    this.refreshTexture(renderComp);
                    RenderComponent.systemHandle(renderComp);
                } else if (renderComp.renderType === RenderType.Spine) {
                    this.refreshSpine(renderComp);
                    RenderComponent.systemHandle(renderComp);
                } else if (renderComp.renderType === RenderType.Prefab) {
                    this.refreshPrefab(renderComp);
                    RenderComponent.systemHandle(renderComp);
                }
            });
        });
    }

    private setSpineAnimation(spine: sp.Skeleton, spineAnimation: string) {
        const anis = spineAnimation.split(',');
        spine.animation = anis[0];
        if (anis.length > 0) {
            let index = 1;
            spine.setCompleteListener(() => {
                if (index < anis.length) {
                    spine.animation = anis[index++];
                }
                if (index >= anis.length) {
                    spine.setCompleteListener(null);
                }
            });
        } else {
            spine.setCompleteListener(null);
        }
    }

    private getBundleAndPathFromUrl(url: string) {
        const arr = url.split(':');
        if (arr.length >= 2) {
            return arr.slice(0, 2);
        }
        return ['resources', arr[0]];
    }

    private refreshSpine(renderComp: RenderComponent) {
        renderComp.renderNode.destroyAllChildren();
        renderComp.renderNode.getComponent(Sprite)?.destroy();
        const spine = renderComp.renderNode.getComponent(sp.Skeleton) || renderComp.renderNode.addComponent(sp.Skeleton);

        // 下载新的spine
        if (renderComp.refreshRender) {
            if (renderComp.spineRenderData) {
                spine.skeletonData = renderComp.spineRenderData;
                spine.loop = renderComp.spineLoop;
                spine.paused = renderComp.spinePaused;
                spine.timeScale = renderComp.spineTimeScale;
                spine.premultipliedAlpha = renderComp.spineAlpha;

                const skin = renderComp.spineSkin || spine['defaultSkin'];
                if (skin) spine.setSkin(skin);

                this.setSpineAnimation(spine, renderComp.spineAnimation);
                RenderComponent.callRenderChanged(renderComp);
            } else if (renderComp.renderUrl) {
                spine.skeletonData = null;
                const renderUrl = renderComp.renderUrl;
                const renderType = renderComp.renderType;
                const [bundle, path] = this.getBundleAndPathFromUrl(renderUrl);
                assetManager.loadBundle(bundle, (error, bundle) => {
                    if (!isValid(spine)) return;
                    if (error) return this.error(error);

                    const onFinish = (error: Error, data: sp.SkeletonData) => {
                        if (!isValid(spine)) return;
                        if (error) return this.error(error);

                        if (renderUrl !== renderComp.renderUrl) return;
                        if (renderType !== renderComp.renderType) return;

                        spine.skeletonData = data;
                        spine.loop = renderComp.spineLoop;
                        spine.paused = renderComp.spinePaused;
                        spine.timeScale = renderComp.spineTimeScale;
                        spine.premultipliedAlpha = renderComp.spineAlpha;

                        const skin = renderComp.spineSkin || spine['defaultSkin'];
                        if (skin) spine.setSkin(skin);

                        this.setSpineAnimation(spine, renderComp.spineAnimation);
                        RenderComponent.callRenderChanged(renderComp);
                    };
                    if (path[path.length - 1] === '/') {
                        bundle.loadDir(path, sp.SkeletonData, (error, data) => {
                            onFinish(error, data[0]);
                        });
                    } else {
                        bundle.load(path, sp.SkeletonData, onFinish);
                    }
                });
            } else {
                spine.skeletonData = null;
            }
        } else if (isValid(spine) && spine.skeletonData) {
            if (renderComp.refreshRenderAttr) {
                spine.loop = renderComp.spineLoop;
                spine.paused = renderComp.spinePaused;
                spine.timeScale = renderComp.spineTimeScale;
                spine.premultipliedAlpha = renderComp.spineAlpha;
            }
            if (renderComp.refreshSpineSkin) {
                const skin = renderComp.spineSkin || spine['defaultSkin'];
                if (skin) spine.setSkin(skin);
            }
            if (renderComp.refreshSpineAnimation) {
                this.setSpineAnimation(spine, renderComp.spineAnimation);
            }
        }
    }

    private refreshTexture(renderComp: RenderComponent) {
        renderComp.renderNode.destroyAllChildren();
        renderComp.renderNode.getComponent(sp.Skeleton)?.destroy();
        const sprite = renderComp.renderNode.getComponent(Sprite) || renderComp.renderNode.addComponent(Sprite);

        if (!renderComp.renderUrl) {
            sprite.spriteFrame = null;
            return;
        }

        // 下载新的图片
        if (renderComp.refreshRender) {
            if (renderComp.texRenderData) {
                sprite.type = renderComp.texRenderType;
                sprite.sizeMode = renderComp.texSizeMode;
                sprite.spriteFrame = renderComp.texRenderData;
                RenderComponent.callRenderChanged(renderComp);
            } else if (renderComp.renderUrl) {
                sprite.spriteFrame = null;
                const renderUrl = renderComp.renderUrl;
                const renderType = renderComp.renderType;
                const [bundle, path] = this.getBundleAndPathFromUrl(renderUrl);
                assetManager.loadBundle(bundle, (error, bundle) => {
                    if (!isValid(sprite)) return;
                    if (error) return this.error(error);

                    const onFinish = (error: Error, frame: SpriteFrame) => {
                        if (!isValid(sprite)) return;
                        if (error) return this.error(error);

                        if (renderUrl !== renderComp.renderUrl) return;
                        if (renderType !== renderComp.renderType) return;

                        sprite.type = renderComp.texRenderType;
                        sprite.sizeMode = renderComp.texSizeMode;
                        sprite.spriteFrame = frame;
                        RenderComponent.callRenderChanged(renderComp);
                    };

                    if (path[path.length - 1] === '/') {
                        bundle.loadDir(path, SpriteFrame, (error, data) => {
                            onFinish(error, data[0]);
                        });
                    } else {
                        bundle.load(path, SpriteFrame, onFinish);
                    }
                });
            } else {
                sprite.spriteFrame = null;
            }
        } else if (isValid(sprite) && sprite.spriteFrame) {
            if (renderComp.refreshRenderAttr) {
                sprite.type = renderComp.texRenderType;
                sprite.sizeMode = renderComp.texSizeMode;
            }
        }
    }

    private refreshPrefab(renderComp: RenderComponent) {
        renderComp.renderNode.getComponent(sp.Skeleton)?.destroy();
        renderComp.renderNode.getComponent(Sprite)?.destroy();

        // 下载新的预制体
        if (renderComp.refreshRender) {
            renderComp.renderNode.destroyAllChildren();
            if (renderComp.prefabRenderData) {
                const node = instantiate(renderComp.prefabRenderData);
                node.parent = renderComp.renderNode;
                RenderComponent.callRenderChanged(renderComp);
            } else if (renderComp.renderUrl) {
                const renderUrl = renderComp.renderUrl;
                const renderType = renderComp.renderType;
                const [bundle, path] = this.getBundleAndPathFromUrl(renderUrl);
                assetManager.loadBundle(bundle, (error, bundle) => {
                    if (!isValid(renderComp.renderNode)) return;
                    if (error) return this.error(error);

                    const onFinish = (error: Error, prefab: Prefab) => {
                        if (!isValid(prefab)) return;
                        if (error) return this.error(error);

                        if (renderUrl !== renderComp.renderUrl) return;
                        if (renderType !== renderComp.renderType) return;

                        const node = instantiate(prefab);
                        node.parent = renderComp.renderNode;
                        RenderComponent.callRenderChanged(renderComp);
                    };

                    if (path[path.length - 1] === '/') {
                        bundle.loadDir(path, Prefab, (error, data) => {
                            onFinish(error, data[0]);
                        });
                    } else {
                        bundle.load(path, Prefab, onFinish);
                    }
                });
            }
        }
    }
}

